與Publisher 相對應,是觀察者模式中的Observer,Publisher在自身狀態改變時,調用Subscriber 的三個不同方法receive(subscription)
、 receive(_:Input)
、 receive(completion:)
來通知Subscriber
Subscriber 訂閱Publisher 後會返回一個遵循
Cancellable
協定的AnyCancellable
,作用上類似於其他響應式框架中的dispose
,控制者訂閱者的釋放,在開發中可以將其作為屬性欄位,在頁面銷毀時,調用AnyCancellable
內部的cancel()
方法進行資源釋放
以下舉一些常見的Subscriber 例子:
sink
Sink
是非常通用的Subscriber
,我們可以自由的處理數據流的狀態,Publisher 還提供了extension func
:sink(receiveCompletion:, receiveValue:)
方法來直接訂閱,為訂閱者連結基於閉包的行為
let integers = (0...3)
integers.publisher
.sink { print("Received \($0)") }
參數:
receiveComplete
: 在完成時執行的閉包receiveValue
: 在收到值時執行的閉包let integers = (0...3)
integers.publisher
.sink(receiveCompletion:{print("Completion: \($0)")},
receiveValue:{ print("Value: \($0)")})
Subject
Subject 是一種 Publisher,你可以調用其 send(_:)
方法將值注入到數據流中,很適合將已有的程式改造成兼容 Combine,有些時候我們想隨時在Publisher 插入值來通知訂閱者,在Rx 中也提供了一個Subject
類型來實現。Subject 通常是一個中間代理,即可以作為Publisher,也可以作為Observer
subscribe(_:Subject)
方法訂閱某個PublisherSubject
的兩個send
方法,我們可以在數據流中隨時插入數據目前在Combine 中,有三個已經實現對Subject
: AnySubject
,CurrentValueSubject
和PassthroughSubject
範例:
// Before
class ContentManager {
var content: [String] {
didSet {
delegate?.contentDidChange(content)
}
}
func getContent() {
content = ["hello", "world"]
}
}
// After
class ContentController {
var content = CurrentValueSubject<[String], NSError>([])
func getContent() {
content.value = ["hello", "world"]
}
}
CurrentValueSubject
的功能很簡單,就是包含一個初始值,並且會在每次值變化的時候發送一個消息,這個值會被保存,可以很方便的用來替代Property Observer。在上例中,以前需要實現delegate 來獲取變化,現在只需要訂閱content 的變化即可,並且它作為一個Publisher,可以很方便的利用操作符進行組合變換
PassthroughSubject
和CurrentValueSubject
幾乎一樣,只是沒有初始值,也不會保存任何值
Assign
Assign
可以很方便地將接收到的值通過KeyPath
設置到指定的Class 上(不支持Struct
),很適合將已有的程式改造成Reactive,將來自Publisher 的每個元素分配給物件中的屬性
參數:
keyPath
:表示要分配屬性的 keyPath。關於 Key-Path 表達式請參考此官方文章
object
:包含此屬性的物件。訂閱者在每次接收到新值時都會分配給該物件的屬性class Student {
let name: String
var score: Int
init(name: String, score: Int) {
self.name = name
self.score = score
}
}
let student = Student(name: "Ryder", score: 91)
print(student.score)
let observer = Subscribers.Assign(object: student, keyPath: \Student.score)
let publisher = PassthroughSubject<Int, Never>()
publisher.subscribe(observer)
publisher.send(95)
print(student.score)
publisher.send(98)
print(student.score)
一旦publisher 的值發生改變,對應的student
的score
也會被更新